-- Copyright (c) 2015,2016 Lucky Byte, Inc.
--
-- Summary : 解析命令行选项
--
-- Author  : 吴小虎 <xiaohu@lucky-byte.com>
-- Since   : 2016.7.2
--
-- History
--  * 2015.7.2 : 创建此文件
--

-- Copyright (c) 2009 Aleksey Cheusov <vle@gmx.net>
--
-- Permission is hereby granted, free of charge, to any person obtaining
-- a copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
--
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

--[[
https://github.com/LuaDist/alt-getopt

lua_altgetopt is a MIT-licenced module for Lua programming language
for processing application's arguments the same way
BSD/GNU getopt_long(3) functions do.

Main features and goals:
1) compatibility to SUS "Utility Syntax Guidelines"
http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
guidelines 3-13.
2) This module is for plain Lua
2) No extra dependencies
3) No hooks, no functional tricks ;-)
4) Support for long options, e.g. compatibility with getopt_long(3) C function
   present in *BSD and GNU libc.
5) Long options may optionally have one-char synonym or other long synonym
6) Strict error checking (checks for an incorrect use of options)
7) alt_getopt.lua is a Lua module.

In particular alt_getopt.lua supports the following
   -kVALUE, -k VALUE, --key=VALUE, --key VALUE,
   -abcd is equivalent to -a -b -c -d if neither of them accept value.
All options must be declared as accepting value or not.

Two ways of options handling are provided.
See alt_getopt application which is a part of regression tests.

Makefile:
It's probably useless for end users, you can copy alt_getopt.lua
to the destination yourself. Otherwise use BSD make,
NetBSD make is preferred because NetBSD is my development platform.

If 'make test' fails on your platform/Lua, please let me know.
If you find a a bug, register it here
http://luaforge.net/project/lua_altgetopt
or send me bugreport directly.

Author: Aleksey Cheusov <vle@gmx.net>
]]

local type, pairs, ipairs, io, os = type, pairs, ipairs, io, os


local function _short2long(opts)
    local i = 1
    local len = #opts
    local ret = {}

    for short_opt, accept_arg in opts:gmatch("(%w)(:?)") do
        ret[short_opt]=#accept_arg
    end
    return ret
end


local function _unknown_opt(opt)
    logger.error("未知选项 `-" .. (#opt > 1 and "-" or "") .. opt .. "'")
end

local function canonize(options, opt)
    if not options[opt] then _unknown_opt(opt) end
    while type(options[opt]) == "string" do
        opt = options[opt]
        if not options[opt] then _unknown_opt(opt) end
    end
    return opt
end


function get_ordered_opts(arg, sh_opts, long_opts)
    local i      = 1
    local count  = 1
    local opts   = {}
    local optarg = {}

    local options = _short2long(sh_opts)
    for k, v in pairs(long_opts) do
        options[k] = v
    end

    while i <= #arg do
        local a = arg[i]
        if a == "--" then
            i = i + 1
            break
        elseif a == "-" then
            break
        elseif a:sub(1, 2) == "--" then
            local pos = a:find("=", 1, true)
            if pos then
                local opt = a:sub(3, pos - 1)
                opt = canonize(options, opt)
                if options[opt] == 0 then
                    logger.error("选项[%s]用法错误.", a)
                end
                optarg[count] = a:sub(pos + 1)
                opts[count] = opt
            else
                local opt = a:sub(3)
                opt = canonize(options, opt)
                if options[opt] == 0 then
                    opts[count] = opt
                else
                    if i == #arg then
                        logger.error("选项[%s]缺少值.", a)
                    end
                    optarg[count] = arg[i + 1]
                    opts[count] = opt
                    i = i + 1
                end
            end
            count = count + 1
        elseif a:sub(1, 1) == "-" then
            local j
            for j = 2, a:len() do
                local opt = canonize(options, a:sub(j, j))
                if options[opt] == 0 then
                    opts[count] = opt
                    count = count + 1
                elseif a:len() == j then
                    if i == #arg then
                        logger.error("选项[%s]缺少值.", opt)
                    end
                    optarg[count] = arg[i + 1]
                    opts[count] = opt
                    i = i + 1
                    count = count + 1
                    break
                else
                    optarg[count] = a:sub(j + 1)
                    opts[count] = opt
                    count = count + 1
                    break
                end
            end
        else
            break
        end
        i = i + 1
    end
    return opts, i, optarg
end


local function getopts(arg, sh_opts, long_opts)
    local ret = {}
    local opts, optind, optarg = get_ordered_opts(arg, sh_opts, long_opts)
    for i, v in ipairs(opts) do
        if optarg[i] then
            ret[v] = optarg[i]
        else
            ret[v] = 1
        end
    end
    return ret, optind
end

return getopts

--[[    测试案例
#!/usr/bin/env lua

require "alt_getopt"

local long_opts = {
   verbose = "v",
   help    = "h",
   fake    = 0,
   len     = 1,
   output  = "o",
   set_value = "S",
   ["set-output"] = "o"
}

local ret
local optarg
local optind
opts,optind,optarg = alt_getopt.get_ordered_opts (arg, "hVvo:n:S:", long_opts)
for i,v in ipairs (opts) do
   if optarg [i] then
      io.write ("option `" .. v .. "': " .. optarg [i] .. "\n")
   else
      io.write ("option `" .. v .. "'\n")
   end
end

optarg,optind = alt_getopt.get_opts (arg, "hVvo:n:S:", long_opts)
for k,v in pairs (optarg) do
   io.write ("fin-option `" .. k .. "': " .. v .. "\n")
end

for i = optind,#arg do
   io.write (string.format ("ARGV [%s] = %s\n", i, arg [i]))
end
]]
